package com.test.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.test.entity.Const;
import com.test.entity.dto.Account;
import com.test.entity.dto.AccountDetails;
import com.test.entity.dto.AccountPrivacy;
import com.test.entity.vo.request.EmailAccountVo;
import com.test.entity.vo.request.ResetConfirmVo;
import com.test.entity.vo.request.ResetPasswordVo;
import com.test.entity.vo.request.ResetPwdVo;
import com.test.mapper.AccountDetailsMapper;
import com.test.mapper.AccountMapper;
import com.test.mapper.AccountPrivacyMapper;
import com.test.service.AccountService;
import com.test.utils.FlowUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j

public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService  {
    @Resource
    AccountMapper accountMapper;

    @Resource
    PasswordEncoder passwordEncoder;
    @Resource
    AmqpTemplate amqpTemplate;
    @Resource
    FlowUtils flowUtils;
    @Resource
    StringRedisTemplate stringRedisTemplate;
    @Resource
    AccountDetailsMapper accountDetailsMapper;
    @Resource
    AccountPrivacyMapper accountPrivacyMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Account account = this.findByUsername(username);

        if(account == null)
            throw new UsernameNotFoundException("用户名或密码错误");
        return User
                .withUsername(account.getUsername())
                .password(account.getPassword())
                .roles(account.getRole())
                .build();


    }


    public Account findByUsername(String username) {
        return this.query().eq("username", username).one();
    }

    /**
     * 生成并发送注册邮箱验证码
     *
     * @param type 用户类型，用于区分不同类型的用户注册
     * @param email 用户输入的邮箱地址
     * @param ip 用户请求的IP地址，用于安全审计或防护
     * @return 返回字符串表示的HTTP状态码，表示验证码发送的状态
     */
    @Override
    public String registerEmailVerifyCode(String type, String email, String ip) {
        if(!checkIp(ip)){
            return "请求过于频繁,请稍后重试";
        }
        // 创建一个随机数生成器
        Random random = new Random();
        // 生成一个六位的验证码
        int code = random.nextInt(899999) + 100000;
        // 创建一个不可变的Map对象，包含验证码及其相关类型和邮箱信息
        Map<String,Object> data = Map.of("type",type,"email",email,"code",code);
        // 使用AMQP模板将验证码信息发送到emailQueue队列中，以便异步处理发送邮件
        amqpTemplate.convertAndSend("emailQueue",data);
        // 将验证码存储到Redis中，键为常量EMAIL_VERIFY_CODE加上邮箱地址，值为验证码，设置过期时间为3分钟
        stringRedisTemplate.opsForValue().set( Const.EMAIL_VERIFY_CODE+email,String.valueOf(code), 20,TimeUnit.MINUTES);
        return null;


    }

    /**
     * 注册邮箱账户
     *
     * 此方法用于处理用户通过邮箱注册账户的请求它首先检查验证码的有效性，
     * 然后验证用户名和邮箱的唯一性，最后在数据库中创建新账户
     *
     * @param vo 包含用户注册信息的对象，包括用户名、邮箱、验证码等
     * @return 返回一个字符串消息，表示注册结果如果注册成功，返回null；
     *         否则返回一个描述错误原因的字符串
     */
    @Override
    public String registerEmailAccount(EmailAccountVo vo) {
        // 获取用户输入的用户名和邮箱
        String username = vo.getUsername();
        String email = vo.getEmail();
        System.out.println(stringRedisTemplate.opsForValue().get(Const.EMAIL_VERIFY_CODE+email));

        // 检查验证码是否存在，如果不存在，提示用户先获取验证码
        if(Boolean.FALSE.equals(stringRedisTemplate.hasKey(Const.EMAIL_VERIFY_CODE + email)))return "请先获取验证码";
        System.out.println(stringRedisTemplate.opsForValue().get(Const.EMAIL_VERIFY_CODE+email));


        // 比较用户输入的验证码与Redis中存储的验证码是否一致，如果不一致，提示用户重新输入
        if(!Objects.equals(stringRedisTemplate.opsForValue().get(Const.EMAIL_VERIFY_CODE + email), String.valueOf(vo.getCode())))return "验证码错误,请重新输入验证码";

        // 检查用户名是否已存在，如果不存在，提示用户用户名已存在
        if(accountMapper.exists(new QueryWrapper<Account>().eq("username",username)))return "用户名已存在";

        // 检查邮箱是否已存在，如果不存在，提示用户邮箱已存在
        if(accountMapper.exists(new QueryWrapper<Account>().eq("email",email)))return "邮箱已存在";

        // 对用户输入的密码进行加密处理
        String password = passwordEncoder.encode(vo.getPassword());
        //         创建一个新账户对象，并设置属性值
        Account account = new Account(username, password, "USER", email, new Date(),null);



        // 在数据库中插入新账户信息，如果插入成功，返回null；否则返回错误信息
        if(this.save(account)) {
            log.info("用户注册成功");
            // 验证码验证通过且用户名与邮箱均唯一后，删除Redis中的验证码
            stringRedisTemplate.delete(Const.EMAIL_VERIFY_CODE+email);
            accountDetailsMapper.insert(new AccountDetails(account.getId()));
            accountPrivacyMapper.insert(new AccountPrivacy(account.getId()));
            return null;
        }

        else return "内部错误，请联系管理员";

    }

    @Override
    public String resetPassword(ResetPasswordVo vo) {
        String email = vo.getEmail();
        if(Boolean.FALSE.equals(stringRedisTemplate.hasKey(Const.EMAIL_VERIFY_CODE + email)))return "请先获取验证码";
        if(!Objects.equals(stringRedisTemplate.opsForValue().get(Const.EMAIL_VERIFY_CODE + email), String.valueOf(vo.getCode()))) return "验证码错误,请重新输入验证码";
        Boolean update = this.update(new UpdateWrapper<Account>().eq("email", email).set("password", passwordEncoder.encode(vo.getPassword())));
        if(update){
            stringRedisTemplate.delete(Const.EMAIL_VERIFY_CODE+email);
            return null;
        }
        return "内部错误，请联系管理员";

    }

    @Override
public String resetEmail(int id, ResetConfirmVo vo) {
    String code = vo.getCode();
    String email = vo.getEmail();
    if (code == null) return "请先获取验证码";
    if (!Objects.equals(stringRedisTemplate.opsForValue().get(Const.EMAIL_VERIFY_CODE + email), String.valueOf(code))) return "验证码错误,请重新输入验证码";

    // 检查新邮箱是否已被其他用户使用
    Account existingAccount = this.getOne(new QueryWrapper<Account>().eq("email", email));
    if (existingAccount != null && !existingAccount.getId().equals(id)) {
        return "邮箱已被其他用户占用";
    }

    // 更新用户邮箱
    this.update().eq("id", id).set("email", email).update();
    return null;
}

    @Override
    public String resetPassword(int id, ResetPwdVo vo) {
        String password = this.query().eq("id", id).one().getPassword();
        if(!passwordEncoder.matches(vo.getOldPassword(), password)){
            return "原来密码填写错误";

        }
        this.update().eq("id", id).set("password", passwordEncoder.encode(vo.getNewPassword())).update();
        return null;


        }



    @Override
    public String resetConfirm(ResetConfirmVo vo) {
        String email = vo.getEmail();
        if(Boolean.FALSE.equals(stringRedisTemplate.hasKey(Const.EMAIL_VERIFY_CODE + email)))return "请先获取验证码";
        if(!Objects.equals(stringRedisTemplate.opsForValue().get(Const.EMAIL_VERIFY_CODE + email), String.valueOf(vo.getCode()))) return "验证码错误,请重新输入验证码";
        return null;
    }

    protected Boolean checkIp(String ip){
        String key = Const.IP_VERIFY_CODE+ip;
        if(flowUtils.checkFlow(key,60)){
            return true;
        }
        return false;
    }



}
